// Copyright (C) CompatibL Technologies LLC. All rights reserved.
// This code contains valuable trade secrets and may be used, copied,
// stored, or distributed only in accordance with a written license
// agreement and with the inclusion of this copyright notice. 

#region File Info
// File       : StringsControl.cs
// Description: ChordCreator.StringsControl user control implementation.
// Package    : ChordCreator 
//
#endregion

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Linq;
using ChordCreator.Model;
using ChordCreator.Services.ConfigurationServices;
using ChordCreator.ViewServices;

namespace ChordCreator.View.StringsFunctionality
{
    /// <summary>
    /// Summary description for StringsControl.
    /// </summary>
    public partial class StringsControl : UserControl
    {
        private const int NoteRadius = 10;
        private const string NoNoteComboText = "X";
        private Color fredColor_;
        private int beginHeight_;
        private int beginWidth_;
        private int endHeight_;
        private int endWidth_;
        private int height_;
        private int width_;
        private ScaleNote[] currentDysplayedNotes_;

        /// <summary>
        /// Initializes a new instance of the <see cref="StringsControl"/> user control.
        /// </summary>
        public StringsControl()
        {
            InitializeComponent();
            string1_.Items.AddRange(Enum.GetNames(typeof(Note)));
            string2_.Items.AddRange(Enum.GetNames(typeof(Note)));
            string3_.Items.AddRange(Enum.GetNames(typeof(Note)));
            string4_.Items.AddRange(Enum.GetNames(typeof(Note)));
            string5_.Items.AddRange(Enum.GetNames(typeof(Note)));
            string6_.Items.AddRange(Enum.GetNames(typeof(Note)));
            string1_.SelectedItem = Note.E.ToString();
            string2_.SelectedItem = Note.B.ToString();
            string3_.SelectedItem = Note.G.ToString();
            string4_.SelectedItem = Note.D.ToString();
            string5_.SelectedItem = Note.A.ToString();
            string6_.SelectedItem = Note.E.ToString();
            pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height);
            DrawHUD();
            ShowNotes(Enum.GetValues(typeof(Note)).Cast<Note>().Select(x => new ScaleNote(1, x)).ToArray());
        }

        public void DrawHUD()
        {
            if (pictureBox1.Image == null)
            {
                return;
            }
            Graphics g = Graphics.FromImage(pictureBox1.Image);
            g.Clear(Color.White);
            beginHeight_ = pictureBox1.Height / 14;
            beginWidth_ = 5;
            endHeight_ = pictureBox1.Height - beginHeight_ - (pictureBox1.Height / 14);
            endWidth_ = pictureBox1.Width - beginWidth_ - (pictureBox1.Width / 14);
            height_ = endHeight_ - beginHeight_;
            width_ = endWidth_ - beginWidth_;


            // Drawing horisontal lines
            for (int i = beginHeight_ + (height_ / HardcodeHelper.StringsCount) / 2; i < endHeight_ - (height_ / HardcodeHelper.StringsCount) / 2; i += height_ / HardcodeHelper.StringsCount)
            {
                g.DrawLine(Pens.Black, new Point(beginWidth_, i), new Point(endWidth_ + (width_ / HardcodeHelper.FredsCount) / 2, i));
            }

            fredColor_ = Color.Black;
            Pen openFredPen = new Pen(fredColor_, 3);
            g.DrawLine(openFredPen, new Point(beginWidth_, beginHeight_), new Point(beginWidth_, endHeight_));

            Pen closedFredPen = new Pen(fredColor_, 1);
            Pen twelveFredPen = new Pen(fredColor_, 2);
            for (int i = 1; i <= HardcodeHelper.FredsCount; i++)
            {
                int coord = beginWidth_ + i * (width_ / HardcodeHelper.FredsCount) + (width_ / HardcodeHelper.FredsCount) / 2;
                Pen pen = closedFredPen;

                if (i == 12)
                {
                    pen = twelveFredPen;
                }
                g.DrawLine(pen, new Point(coord, beginHeight_), new Point(coord, endHeight_));
            }

            Font font = new Font("Times New Roman", HardcodeHelper.FredFontHeight);
            int xOffset = (width_ / HardcodeHelper.FredsCount) / 4 + (width_ / HardcodeHelper.FredsCount) / 2;
            Brush textBrush = Brushes.Black;

            g.DrawString("V", font, textBrush, beginWidth_ + 4 * (width_ / HardcodeHelper.FredsCount) + xOffset, endHeight_);
            g.DrawString("IX", font, textBrush, beginWidth_ + 8 * (width_ / HardcodeHelper.FredsCount) + xOffset - 3, endHeight_);
            g.DrawString("XII", font, textBrush, beginWidth_ + 11 * (width_ / HardcodeHelper.FredsCount) + xOffset - 7, endHeight_);
            g.DrawString("XIV", font, textBrush, beginWidth_ + 14 * (width_ / HardcodeHelper.FredsCount) + xOffset - 7, endHeight_);
        }

        /// <summary>
        /// Calculating note position notes
        /// </summary>
        /// <returns>Center of drawedNote </returns>
        internal Point CalcNote(int noteString, int noteFred)
        {
            Point result;
            int y;
            int x;
            int contourWidth = 1;
            if (noteFred == 0)
            {
                x = beginWidth_;
            }
            else
            {
                x =  beginWidth_ + noteFred*(width_/HardcodeHelper.FredsCount);
            }
            y = beginHeight_ + (height_ / HardcodeHelper.StringsCount) / 2 + (noteString - 1) * (height_ / HardcodeHelper.StringsCount);
            int dx = x - NoteRadius + contourWidth;
            int dy = y - NoteRadius + contourWidth;
            result = new Point(dx + NoteRadius, dy + NoteRadius);
           
           
            return result;
        }

        /// <summary>
        /// Drawing notes
        /// </summary>
        /// <returns>Center of drawedNote </returns>
        internal void DrawNote(Color? fill, Color? gradientFill, ScaleNote note, Point noteCenter)
        {
            Graphics g = Graphics.FromImage(pictureBox1.Image);
            Color borderColor = Color.Black;
            if (note != null)
            switch (note.Degree)
            {
                case 1:
                    borderColor = Color.DarkRed;
                    break;
                case 3:
                    borderColor = Color.LightGreen;
                    break;
                case 5:
                    borderColor = Color.DarkGreen;
                    break;
                case 7:
                    borderColor = Color.DarkBlue;
                    break;
            }

            int contourWidth = 1;
            Pen contourPen = new Pen(borderColor, contourWidth);
            // TODO: tonics
//            g.DrawEllipse(contourPen, noteCenter.X - NoteRadius, noteCenter.Y - NoteRadius, NoteRadius * 2, NoteRadius * 2);
            int dx = noteCenter.X - NoteRadius + contourWidth;
            int dy = noteCenter.Y - NoteRadius + contourWidth;
            int size = 2 * (NoteRadius - contourWidth);
            if (gradientFill != null)
            {
                Rectangle rect = new Rectangle(new Point(dx, dy), new Size(size, size));
                Brush brush = new LinearGradientBrush(rect,
                                                      fill.Value, gradientFill.Value, 360);
                g.FillEllipse(brush, dx, dy, size, size);
            }
            else
                if (fill != null)
                {
                    Brush brush = new SolidBrush(fill.Value);
                    g.FillEllipse(brush, dx, dy, size, size);
                }
            if (note != null)
            {
                Font font = new Font("Times New Roman", 10, FontStyle.Bold);
                Brush fontBrush = new SolidBrush(Color.Black);
                string noteName = CurrentConfiguration.Instance.GetNotePresentation(note.Note);
                int noteNameX = dx;
                if (noteName.Length == 1)
                {
                    noteNameX += 3;
                }
                int noteNameY = dy + 2;
                g.DrawString(noteName, font, fontBrush, noteNameX, noteNameY);
            }
            return;
        }
         
        private void Redraw()
        {
            DrawHUD();
            ShowNotes(currentDysplayedNotes_);
        }

        internal void ShowNotes(ScaleNote[] notes)
        {
            if (notes == null)
            {
                return;
            }
            currentDysplayedNotes_ = notes;
            Pen contourPen = new Pen(Color.Yellow, 2);

            ColorPool colorPool = new ColorPool(false);
            StringedNoteManager manager = new StringedNoteManager();
            // Drawing notes
            foreach (ScaleNote note in notes)
            {
                Color gradientFill = colorPool.GetColor();
                for (int i=1; i<=HardcodeHelper.StringsCount; i++)
                {
                    Note? supposedNote = ResloveNoteForFred(i);
                    if (supposedNote != null)
                    {
                        Note tonic = supposedNote.Value;
                        int fred = note.Note - tonic;
                        if (fred < 0)
                        {
                            fred += 12;
                        }
                        while (fred <= HardcodeHelper.FredsCount)
                        {
                            Point center = CalcNote(i, fred);
                            manager.AddDrawedNote(i, fred, note, gradientFill, center);
                            fred += 12;
                        }
                    }
                }
            }
            // Drawing lines
            List<StringedNoteManager.StringedNoteLine> lines = new List<StringedNoteManager.StringedNoteLine>();
            StringedNoteManager.StringedNoteLine line = manager.GetNextLine(0);
            ColorPool lineColorPool = new ColorPool(false);

            Graphics g = Graphics.FromImage(pictureBox1.Image);
            //Drawing lines
            while (line != null)
            {
                lines.Add(line);
                Point beginLine = line.NoteCenterList[0];
                Color color = lineColorPool.GetColor();
                line.Color = color;
                Pen pen = new Pen(color, 3);
                for (int i = 1; i<line.NoteCenterList.Count; i++)
                {
                    Point endLine = line.NoteCenterList[i];

                    g.DrawLine(pen, beginLine, endLine);
                    beginLine = endLine;
                }
                
                line = manager.GetNextLine(0);

            }
            //Drawing circles
            foreach (var drawline in lines)
            {
                for (int i = 0; i < drawline.NoteCenterList.Count; i++)
                {
                    DrawNote(drawline.Color, drawline.Color, drawline.NoteList[i], drawline.NoteCenterList[i]);
                } 
            }
           
            pictureBox1.Refresh();
        }

       

        private Note? ResloveNoteForFred(int fred)
        {
            ComboBox box;
            switch (fred)
            {
                case 1: box = string1_; break;
                case 2: box = string2_; break;
                case 3: box = string3_; break;
                case 4: box = string4_; break;
                case 5: box = string5_; break;
                case 6: box = string6_; break;
                default: throw new ArgumentException();
            }
            if (box.Text == NoNoteComboText)
                return null;
            return (Note)Enum.Parse(typeof(Note), box.SelectedItem.ToString());
        }

        private void stringCmbBox__MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                ComboBox box = (ComboBox) sender;
                if (box.Text != NoNoteComboText)
                {
                    box.Text = NoNoteComboText;
                    
                }
                else
                {
                    box.Text = box.SelectedItem.ToString();
                }
                Redraw();
            }
        }

        private void stringCmbBox__SelectedIndexChanged(object sender, EventArgs e)
        {
            Redraw();
        }

        private void cpyToClipBoardButton__Click(object sender, EventArgs e)
        {
            Clipboard.SetImage(pictureBox1.Image);
        }

        internal void SetLinesFormat(Dictionary<int, List<ScaleNote>> dictionary)
        {
            DrawHUD();
            if (currentDysplayedNotes_ == null)
            {
                return;
            }

            ColorPool colorPool = new ColorPool(false);
            StringedNoteManager manager = new StringedNoteManager();
            // Drawing notes
            foreach (ScaleNote note in currentDysplayedNotes_)
            {
                Color gradientFill = colorPool.GetColor();
                for (int i = 1; i <= HardcodeHelper.StringsCount; i++)
                {
                    Note? supposedNote = ResloveNoteForFred(i);
                    if (supposedNote != null)
                    {
                        Note tonic = supposedNote.Value;
                        int fred = note.Note - tonic;
                        if (fred < 0)
                        {
                            fred += 12;
                        }
                        while (fred <= HardcodeHelper.FredsCount)
                        {
                            Point center = CalcNote(i, fred);
                            manager.AddDrawedNote(i, fred, note, gradientFill, center);
                            fred += 12;
                        }
                    }
                }
            }
            manager.InitializeLineGear(dictionary);
            // Drawing lines
            List<StringedNoteManager.StringedNoteLine> lines = new List<StringedNoteManager.StringedNoteLine>();
            int degreeOffset = 0;
            StringedNoteManager.StringedNoteLine line = manager.GetNextLine(degreeOffset);
            ColorPool lineColorPool = new ColorPool(false);

            Graphics g = Graphics.FromImage(pictureBox1.Image);
            //Drawing lines
            while (line != null)
            {
                if (line.NoteStringList.Count == 0)
                {
                    break;
                }
                lines.Add(line);
                Point beginLine = line.NoteCenterList[0];
                Color color = lineColorPool.GetColor();
                line.Color = color;
                Pen pen = new Pen(color, 3);
                for (int i = 1; i < line.NoteCenterList.Count; i++)
                {
                    Point endLine = line.NoteCenterList[i];

                    g.DrawLine(pen, beginLine, endLine);
                    beginLine = endLine;
                }
                degreeOffset++;
                line = manager.GetNextLine(degreeOffset);

            }
            //Drawing circles
            foreach (var drawline in lines)
            {
                for (int i = 0; i < drawline.NoteCenterList.Count; i++)
                {
                    DrawNote(drawline.Color, drawline.Color, drawline.NoteList[i], drawline.NoteCenterList[i]);
                }
            }

            pictureBox1.Refresh(); 
        }
    }
}